package com.sys.object.common.utils;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.beanutils.converters.IntegerConverter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
 * bean属性的工具类 
 * @project  name  : basic_core
 * @author   name  : wangchao   
 * @create   time  : 2016-4-5 上午10:09:45  
 * @class    name  : cn.com.basic.common.beanutil.BasePropertyUtils  
 * @modify   list  : (modify by :modify date:modify content)       
 *
 */
public class BasePropertyUtils {

	private static Log logger = LogFactory.getLog(BasePropertyUtils.class);

	private static DataTypeCast typeCast = new DataTypeCast();

	static {
		ConvertUtils.register(new IntegerConverter(null), Integer.class);
	}

	public static List<String> getSetters(Object bean) {
		List setters = new ArrayList();
		PropertyDescriptor[] descriptors = PropertyUtils
				.getPropertyDescriptors(bean);
		for (int i = 0; i < descriptors.length; i++) {
			PropertyDescriptor desc = descriptors[i];
			if (desc.getWriteMethod() != null) {
				String name = desc.getName();
				setters.add(name);
			}
		}
		return setters;
	}

	public static String[] getProperties(Object bean) {
		Map map = getPropertiesMap(bean);
		String[] propNames = (String[]) map.keySet().toArray(new String[0]);

		return propNames;
	}

	public static Map getPropertiesMap(Object bean) {
		Map map = null;

		if ((bean instanceof Map))
			map = (Map) bean;
		else {
			try {
				map = PropertyUtils.describe(bean);
			} catch (Exception e) {
				logger.error("获得bean描述信息出错");
				throw new RuntimeException(e);
			}

		}

		map.remove("class");
		return map;
	}

	public static void copyProperties(Object dest, Object source) {
		try {
			if (dest.getClass().equals(source.getClass())) {
				PropertyUtils.copyProperties(dest, source);
			} else
				copyProperties(dest, source, null, null);
		} catch (Exception e) {
			String info = "属性拷贝失败";
			logger.error(info);
			throw new RuntimeException(info, e);
		}
	}

	public static void copyProperties(Object dest, Object source,
			String destPrefix, String sourcePrefix) {
		try {
			if ((source instanceof Object[])) {
				Object[] sourceArray = (Object[]) source;
				copyProperties(dest, sourceArray);
				return;
			}

			Map mapSource = null;
			if ((source instanceof Map)) {
				mapSource = (Map) source;
			} else {
				mapSource = PropertyUtils.describe(source);
			}

			mapSource.remove("class");

			List destSetters = getSetters(dest);

			List getters = new ArrayList();
			List setters = new ArrayList();
			for (Iterator it = mapSource.keySet().iterator(); it.hasNext();) {
				String keyNameSource = (String) it.next();
				String keyNameDest = keyNameSource;

				if (sourcePrefix != null) {
					keyNameDest = keyNameSource
							.substring(sourcePrefix.length());
				}
				if (destPrefix != null) {
					keyNameDest = destPrefix + keyNameDest;
				}

				if (destSetters.contains(keyNameDest)) {
					getters.add(keyNameSource);
					setters.add(keyNameDest);
				}
			}

			copyProperties(dest, setters, source, getters);
		} catch (Exception e) {
			logger.error("获得bean描述信息出错");
			throw new RuntimeException(e);
		}
	}

	public static void copyProperties(Object dest, List<String> setters,
			Object source, List<String> getters) {
		if ((setters == null) || (getters == null))
			return;

		if ((setters.size() > 0) && (getters.size() > 0)) {
			String[] propNamesSource = new String[getters.size()];
			String[] propNamesDest = new String[setters.size()];

			getters.toArray(propNamesSource);
			setters.toArray(propNamesDest);

			copyProperties(dest, propNamesDest, source, propNamesSource);
		}
	}

	public static <E> void copyProperties(List<Object> destList,
			List<?> sourceList, Class<E> destClass) {
		try {
			for (int i = 0; i < sourceList.size(); i++) {
				Object o = destClass.newInstance();
				copyProperties(o, sourceList.get(i));
				destList.add(o);
			}
		} catch (Exception e) {
			logger.error("拷贝list时出错");
			throw new RuntimeException(e);
		}
	}

	public static void copyProperties(Object dest, Object[] sourceValues) {
		Class destValueClassType = null;
		Object destValue = null;
		String destPropertyName = null;

		Field[] fields = dest.getClass().getDeclaredFields();

		int maxIndex = fields.length > sourceValues.length ? sourceValues.length
				: fields.length;

		for (int i = 0; i < maxIndex; i++) {
			destPropertyName = fields[i].getName();

			destValueClassType = getPropertyType(dest, destPropertyName);

			destValue = typeCast.cast(sourceValues[i], destValueClassType);

			setPropertyValue(dest, destPropertyName, destValue);
		}
	}

	public static void copyProperties(Object dest, String[] setters,
			Object source, String[] getters) {
		if (setters.length != getters.length) {
			logger.error("属性字段数组大小不一致");
			logger.error("源对象:" + source);
			logger.error("目标对象:" + dest);
			throw new RuntimeException("属性字段数组大小不一致");
		}

		for (int i = 0; i < getters.length; i++)
			copyProperties(dest, setters[i], source, getters[i]);
	}

	public static void copyProperties(Object dest, String setter,
			Object source, String getter) {
		Class destValueClassType = null;
		Object destValue = null;

		Object sourceValue = getPropertyValue(source, getter);
		if (sourceValue != null) {
			destValueClassType = getPropertyType(dest, setter);
			if (destValueClassType == null)
				destValueClassType = sourceValue.getClass();

			destValue = typeCast.cast(sourceValue, destValueClassType);
		}

		setPropertyValue(dest, setter, destValue);
	}

	public static  Class getPropertyType(Object bean, String propertyName) {
		Class type = null;
		try {
			if ((bean instanceof Map)) {
				Object value = ((Map) bean).get(propertyName);
				if (value != null)
					type = value.getClass();
			} else {
				type = PropertyUtils.getPropertyType(bean, propertyName);
			}

			return type;
		} catch (Exception e) {
			logger.error("get属性类型时出错,属性:" + propertyName);
			throw new RuntimeException(e);
		}
	}

	public static boolean exists(Object bean, String propertyName) {
		try {
			boolean exists = false;

			if ((bean instanceof Map)) {
				exists = ((Map) bean).containsKey(propertyName);
			} else {
				exists = PropertyUtils.isWriteable(bean, propertyName);
			}

			return exists;
		} catch (Exception e) {
			logger.error("判断属性是否存在时出错,属性:" + propertyName);
			throw new RuntimeException(e);
		}
	}

	public static Object getPropertyValue(Object bean, String propertyName) {
		Object value = null;
		try {
			if (propertyName.indexOf(".") > 0) {
				String[] props = propertyName.split("\\.");
				int n = props.length;
				for (int i = 0; i < n; i++) {
					if (i == 0)
						value = bean;
					if (value == null)
						return null;

					value = getPropertyValue(value, props[i]);
				}
			} else {
				value = PropertyUtils.getProperty(bean, propertyName);
			}
		} catch (NoSuchMethodException e) {
			char ch = propertyName.charAt(1);
			if ((ch >= 'A') && (ch <= 'Z')) {
				propertyName = upperFirstLetter(propertyName);
				try {
					value = PropertyUtils.getProperty(bean, propertyName);
				} catch (IllegalAccessException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				} catch (InvocationTargetException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				} catch (NoSuchMethodException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
			}

		} catch (Exception e) {
			if (bean != null) {
				logger.error("从" + bean.getClass().getName() + " 里get属性时出错:"
						+ propertyName);
			}
			throw new RuntimeException(e);
		}

		return value;
	}

	public static void setPropertyValue(Object bean, String propertyName,
			Object value) {
		try {
			if (!(bean instanceof Map)) {
				Class destValueClassType = getPropertyType(bean, propertyName);
				if (destValueClassType != null) {
					value = typeCast.cast(value, destValueClassType);
				}
			}

			try {
				PropertyUtils.setProperty(bean, propertyName, value);
			} catch (NoSuchMethodException e) {
				char ch = propertyName.charAt(1);
				if ((ch >= 'A') && (ch <= 'Z')) {
					propertyName = upperFirstLetter(propertyName);

					PropertyUtils.setProperty(bean, propertyName, value);
				} else {
					throw new NoSuchMethodException(propertyName);
				}
			}
		} catch (Exception e) {
			logger.error("set时出错,属性:" + propertyName + " 值:" + value);
			throw new RuntimeException(e);
		}
	}

	private static String upperFirstLetter(String propertyName) {
		String name = propertyName.substring(0, 1).toUpperCase()
				+ propertyName.substring(1);
		return name;
	}

	public static void setPropertyValue(Object bean, String propertyName,
			Object value, boolean overwriteNotNull) {
		Object originValue = getPropertyValue(bean, propertyName);

		if ((overwriteNotNull) || (originValue == null))
			setPropertyValue(bean, propertyName, value);
	}
}
